use crate::db::init_db_conn;
use crate::search_engine::init_search_engine;
use app_writer::AppResult;
use db::db_get;
use search_engine::rebuild_index;
use services::clean_up;
use tokio::signal;
use tracing::{info, warn};
use crate::middleware::{handle_40x::handle_40x,cors::cors_middleware};
use crate::routers::router;
use config::{CERT_KEY, CFG};
use salvo::server::ServerHandle;
use salvo::catcher::Catcher;
use salvo::conn::rustls::{Keycert, RustlsConfig};
use salvo::prelude::*;
mod app_error;
mod app_writer;
mod config;
mod db;
mod dtos;
mod services;
mod utils;
mod entities;
mod middleware;
mod routers;
mod search_engine;

#[tokio::main]
async fn main() {
    //同一时间，日志只输出到终端或文件
    let _guard = clia_tracing_config::build()
        .filter_level(&CFG.log.filter_level)
        .with_ansi(CFG.log.with_ansi)
        .to_stdout(CFG.log.to_stdout)
        .directory(&CFG.log.directory)
        .file_name(&CFG.log.file_name)
        .rolling(&CFG.log.rolling)
        .init();
    tracing::info!("log level: {}", &CFG.log.filter_level);

    // 初始化数据库连接和搜索引擎
    let _ = init_db_conn().await;
    let _ = init_search_engine().await;
    let _ = tokio::spawn(rebuild_index());
    // 初始化管理员
    if let Some(admin) = &CFG.admin {
        info!("初始化管理员");
        let _ = init_admin(&admin.username, &admin.password).await;
    } else {
        warn!("初始化管理员未设置");
    }
    // 初始化定时任务
    let _ = tokio::spawn(async {
        tracing::info!("start up clean_up task");
        loop{
            tokio::time::sleep(std::time::Duration::from_secs(60 * 60 * 24)).await;
            clean_up().await
        }
    });
    // 初始化路由
    let _cors_handler = cors_middleware();
    let router = router();
    let service: Service = router.into();
    let service = service.catcher(Catcher::default().hoop(_cors_handler).hoop(handle_40x));//.hoop(_cors_handler).hoop(handle_404));
    println!("🌪️ {} 正在启动 ", &CFG.server.name);
    println!("🔄 在以下位置监听 {}", &CFG.server.address);
    
    match CFG.server.ssl {
        true => {
            println!(
                "📖 Open API页面: https://{}/scalar",
                &CFG.server.address.replace("0.0.0.0", "127.0.0.1")
            );
            let config = RustlsConfig::new(
                Keycert::new()
                    .cert(CERT_KEY.cert.clone())
                    .key(CERT_KEY.key.clone()),
            );
            let acceptor = TcpListener::new(&CFG.server.address)
                .rustls(config)
                .bind()
                .await;
            let server = Server::new(acceptor);
            let handle = server.handle();
            tokio::spawn(shutdown_signal(handle));
            server.serve(service).await;
         }
        false => {
            println!(
                "📖 Open API页面: http://{}/scalar",
                &CFG.server.address.replace("0.0.0.0", "127.0.0.1")
            );
            let acceptor = TcpListener::new(&CFG.server.address).bind().await;
            let server = Server::new(acceptor);
            let handle = server.handle();
            tokio::spawn(shutdown_signal(handle));
            server.serve(service).await;
            }
    }
}

async fn init_admin(username: &str, password: &str) -> AppResult<()> {
    let db = db_get()?;
    let rec = sqlx::query!(
        r#"
        SELECT id FROM users
        WHERE admin = true;
    "#)
    .fetch_all(db)
    .await?;
    if rec.is_empty() {
        let _ = sqlx::query!(
            r#"
            INSERT INTO users (username, password, admin)
            VALUES ($1, $2, true);
        "#,
            username,
            password
        )
        .execute(db)
        .await?;
    } else {
        info!("管理员已存在，跳过初始化");
    }
    Ok(())
}


async fn shutdown_signal(handle: ServerHandle) {
    let ctrl_c = async {
        signal::ctrl_c()
            .await
            .expect("failed to install Ctrl+C handler");
    };

    #[cfg(unix)]
    let terminate = async {
        signal::unix::signal(signal::unix::SignalKind::terminate())
            .expect("failed to install signal handler")
            .recv()
            .await;
    };

    #[cfg(not(unix))]
    let terminate = std::future::pending::<()>();

    tokio::select! {
        _ = ctrl_c => info!("ctrl_c signal received"),
        _ = terminate => info!("terminate signal received"),
    }
    handle.stop_graceful(std::time::Duration::from_secs(60));
}

#[cfg(test)]
mod tests {
    use salvo::prelude::*;
    use salvo::test::{ResponseExt, TestClient};

    use crate::config::CFG;

    #[tokio::test]
    async fn test_hello_world() {
        let service = Service::new(super::router());

        let content = TestClient::get(format!(
            "http://{}",
            &CFG.server.address.replace("0.0.0.0", "127.0.0.1")
        ))
        .send(&service)
        .await
        .take_string()
        .await
        .unwrap();
        assert_eq!(content, "Hello World from salvo");
    }
}
